import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class SendImmediatelyScheduler implements Scheduler {

	double V = 0;
	int bandwidth = 0;
	int time_slot = 1;
	double power = 0;
	int slot_number = 100000;

	private int group_number = 3;
	private Group[] group_list;
	private BufferedReader signal_reader;

	private double energy_consumption = 0;
	private int totally_delay = 0;
	private double signal_strength = 0;
	private Recorder recorder;

	private double queue = 0;
	public int data = 0;
	private double metric_considering_app_char = 0;

	private int overdue = 0;
	private double queue_overdue = 0;

	int queue_delay = 0;
	double queue_performance_metric = 0;

	double energy_consumption_each[];

	double TailEnergy = 0;
	double TailPower = 0;
	int last_send = -100;

	public SendImmediatelyScheduler() {

		group_list = new Group[group_number];
		recorder = new Recorder("-immediately");

		energy_consumption_each = new double[group_number];

		// 第一个参数为deadline,第二个参数为上层应用每个时隙发送数据量的平均值（poisson分布）
		group_list[0] = new Group(10, 60, 0, 1, recorder);
		group_list[1] = new Group(200, 60, 1, 1, recorder);
		group_list[2] = new Group(5000, 60, 2, 1, recorder);

		try {
			signal_reader = new BufferedReader(new FileReader(new File("C:\\Users\\LZQ\\Desktop\\Lypunov_simulation_data\\signal_vector.csv")));
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	@Override
	public void begin_scheduling(int slot_number) {
		// TODO Auto-generated method stub
		this.slot_number = slot_number;
		for (int t = 0; t < slot_number; t += time_slot) {
			// System.out.println("Time slot: " + t);
			// 读取信号强度，获得带宽，功率等参数
			try {
				signal_strength = Double.parseDouble(signal_reader.readLine());
			} catch (NumberFormatException | IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			V = getV();
			power = getPower();
			bandwidth = getBandwidth();
			TailPower = getTailPower(t);
			for (Group gp : group_list)
				gp.refreshState(t);

			// 发送决策
			int allocated_bandwidth = 0;
			recorder.recordlog(new StringBuffer("###########bandwidth: " + bandwidth + "\n"));
			while (allocated_bandwidth < bandwidth) {
				// 每一次找出最早到达的那个数据单元进行发送
				int arr_time = Integer.MAX_VALUE;
				DataUnit earliest_du = null;
				Group earliest_gp = null;
				for (int i = 0; i < group_number; i++) {
					for (DataUnit du : group_list[i].DataQueue) {
						if (du.arr_time < arr_time) {
							earliest_du = du;
							earliest_gp = group_list[i];
							arr_time = du.arr_time;
						}
					}
				}
				// arr_time未被修改，说明队列中没有数据单元,不发送
				if (arr_time == Integer.MAX_VALUE)
					break;
				// System.err.println("slot: "+t+" V: "+V+" Signal Strength: "+signal_strength+" Power: "+power+" BandWidth: "+bandwidth+" .");
				if (earliest_du.data.length > (bandwidth - allocated_bandwidth)) {
					// 如果剩下的可分配的带宽不足以发送这个数据单元中的数据了
					// 实际发送的数据量为：bandwidth-allocated_bandwidth
					earliest_du.data = new byte[earliest_du.data.length - (bandwidth - allocated_bandwidth)];
					earliest_gp.queue_length[earliest_du.delay_level] -= (bandwidth - allocated_bandwidth);
					data += bandwidth - allocated_bandwidth;

					energy_consumption_each[earliest_gp.ID] += power * (bandwidth - allocated_bandwidth) / (1.0 * bandwidth);
					energy_consumption += power * (bandwidth - allocated_bandwidth) / (1.0 * bandwidth);

					// if ((t - earliest_du.arr_time) > earliest_du.bound) {
					// // metric_considering_app_char += (bandwidth - allocated_bandwidth) * (t - earliest_du.arr_time) *
					// earliest_gp.max_penalty_factor;
					// overdue+=(bandwidth - allocated_bandwidth);
					// } else
					{
						metric_considering_app_char += earliest_gp.omega * (bandwidth - allocated_bandwidth) * (t - earliest_du.arr_time)
											* earliest_gp.omega;
					}
					totally_delay += (bandwidth - allocated_bandwidth) * (t - earliest_du.arr_time);
					allocated_bandwidth = bandwidth;
					// System.out.println("@send " + (bandwidth - allocated_bandwidth) + " byte");
					recorder.recordlog(new StringBuffer("###########send " + (bandwidth - allocated_bandwidth) + " bytes data.  cause delay: "
										+ ((bandwidth - allocated_bandwidth) * (t - earliest_du.arr_time)) + "\n"));
				} else {
					// 如果剩下的带宽足以将整个数据单元的数据发送完毕
					earliest_gp.queue_length[earliest_du.delay_level] -= earliest_du.data.length;
					allocated_bandwidth += earliest_du.data.length;
					// System.out.println("#send " + (earliest_du.data.length) + " byte");
					recorder.recordlog(new StringBuffer("###########send " + (earliest_du.data.length) + " bytes data.cause delay: "
										+ (earliest_du.data.length * (t - earliest_du.arr_time)) + "\n"));
					data += earliest_du.data.length;

					energy_consumption_each[earliest_gp.ID] += power * (earliest_du.data.length) / (1.0 * bandwidth);
					energy_consumption += power * (earliest_du.data.length) / (1.0 * bandwidth);

					// if ((t - earliest_du.arr_time) > earliest_du.bound) {
					// // metric_considering_app_char += earliest_du.data.length *(t - earliest_du.arr_time)*
					// earliest_gp.max_penalty_factor;
					// overdue+=earliest_du.data.length;
					// } else
					{
						metric_considering_app_char += earliest_gp.omega * earliest_du.data.length * (t - earliest_du.arr_time) * earliest_gp.omega;
					}
					totally_delay += earliest_du.data.length * (t - earliest_du.arr_time);
					// 判断这个数据单元是否超过了deadline
					// if ((t - earliest_du.arr_time) > earliest_du.bound)
					// overdue++;
					// 数据单元发送完毕，从队列中移除
					earliest_gp.DataQueue.remove(earliest_du);

				}
			}

			if (allocated_bandwidth > 0)// 如果分配了带宽，那么说明有数据发送了，能耗增加
			{
				last_send = t;
				TailEnergy += TailPower;
			}

			// 更新总队长
			queue = 0;
			for (Group gp : group_list) {
				for (int q : gp.queue_length)
					queue += q;
			}
			// 上面计算的是由发送数据包产生的时延和性能损失
			// 计算缓存在队列中的所有数据单元的时延、性能损失
			queue_delay = 0;
			queue_performance_metric = 0;
			int queue_data = 0;
			queue_overdue = 0;
			for (int i = 0; i < group_number; i++) {
				for (DataUnit du : group_list[i].DataQueue) {
					queue_delay += (t - du.arr_time) * du.data.length;
					// if ((t - du.arr_time) > du.bound) {
					// //queue_performance_metric += du.delay * group_list[i].max_penalty_factor * du.data.length;
					// queue_overdue+=du.data.length;
					// } else
					{
						queue_performance_metric += group_list[i].omega * du.delay * group_list[i].omega * du.data.length;
					}
					queue_data += du.data.length;
					// if ((t - du.arr_time) > du.bound)
					// queue_overdue++;
				}
			}

			// totally_delay+=queue_delay;
			// metric_considering_app_char+=queue_performance_metric;

			// recorder.recordQueueLength(queue);
			// recorder.recordEnergy(energy_consumption);
			// recorder.recordDelay(totally_delay + queue_delay);
			// recorder.recordMetric(metric_considering_app_char + queue_performance_metric);
			// recorder.recordDelayPerByte(1.0 * (totally_delay + queue_delay) / (data + queue_data));
			// recorder.recordSendData(data);
			// recorder.recordOverdue(queue_overdue + overdue);
		}
		int generated_data_number = 0;
		for (int i = 0; i < group_number; i++)
			generated_data_number += group_list[i].app.generated_data_size;
		recorder.flush();
		System.out.println("Energy Consumption: " + (int) (energy_consumption + TailEnergy) + " J");
		System.out.println("Tail Energy: " + (int) (TailEnergy) + " J");
		System.out.println("Totally dealy: " + (totally_delay + queue_delay) + " s*byte");
		System.out.println("Totally metric considering app: " + (metric_considering_app_char + queue_performance_metric) + " s*byte*(1/omega)");
		System.out.println("Current Queue Length: " + queue);
		System.out.println("Generated Data Size: " + generated_data_number);
		System.out.println("Overdue number: " + (overdue + queue_overdue));
		System.out.println("Delay per byte: " + (1.0 * (totally_delay + queue_delay) / generated_data_number));
		System.out.println("Totally send data: " + (int) data + " byte\n");
	}

	public double getV() {
		return 1.2;

	}

	public double getPower() {
		return -25 * signal_strength - 1025;

	}

	public double getTailPower(int t) {
		if(last_send==-100)
			return 0;
		if ((t - last_send) > 7) {
			// 计算完整的一个tail
			return 732 * 3 + 388 * 4;
		} else if ((t - last_send) > 3) {
			return (t - last_send - 3 - 1) * 388 + 732 * 3;

		} else {
			return (t - last_send - 1) * 732;
		}
	}

	public int getBandwidth() {
		return (int) ((0.909 * signal_strength + 153.63) * 3);

	}

	@Override
	public void setV(double v) {
		// TODO Auto-generated method stub

	}

}
